#include <host_syscall.h>
#include <proc.h>
#include <memlayout.h>
#include <stdio.h>
#include <stub.h>
#include <trap.h>
#include <vmm.h>
#include <um.h>

static int
host_syscall_in_child (struct proc_struct *proc)
{
	struct user_regs_struct regs;
	int err, pid = proc->arch.host->host_pid;

	err = syscall4 (__NR_ptrace, PTRACE_GETREGS, pid, 0, (long)&regs);
	if (err < 0) {
		cprintf ("host_syscall_in_child: cannot get regs\n");
		return -1;
	}
	
	regs.eip = STUB_CODE + (unsigned long)stub_exec_syscall - (unsigned long) &__syscall_stub_start;
	err = syscall4 (__NR_ptrace, PTRACE_SETREGS, pid, 0, (long)&regs);
	if (err < 0) {
		cprintf ("host_syscall_in_child: cannot set regs\n");
		return -1;
	}
		
	err = syscall4 (__NR_ptrace, PTRACE_CONT, pid, 0, 0);
	if (err < 0) {
		cprintf ("host_syscall_in_child: cannot continue the child\n");
		return -1;
	}
	err = wait_stub_done (pid);
	if (err < 0) {
		cprintf ("host_syscall_in_child: wait stub done failed with err = %d\n", err);
		return -1;
	}
	err = current_stub_frame(proc->arch.host->stub_stack)->eax;
	
	return err;
}

int
host_mmap (struct proc_struct *proc,
		   void *addr, size_t length, int prot, int flags,
		   int fd, uint32_t offset)
{
	//cprintf ("host_mmap: 0x%x, 0x%x, %d, %d, %d, 0x%x\n", (long)addr, length, prot, flags, fd, offset);

	struct stub_stack *stub_stack = proc->arch.host->stub_stack;
	struct stub_frame *frame = (current_stub_frame (stub_stack));
	
	frame->eax = __NR_mmap;
	frame->ebx = STUB_DATA + (unsigned long)frame->data - (unsigned long)(stub_stack);
	struct mmap_arg_struct* args = (struct mmap_arg_struct *)frame->data;
	args->addr = (long)addr;
	args->len = length;
	args->prot = prot;
	args->flags = flags;
	args->fd = fd;
	args->offset = offset;
	return host_syscall_in_child (proc);
}

int
host_munmap (struct proc_struct *proc,
			 void *addr, size_t length)
{
	//cprintf ("host_munmap: 0x%x, 0x%x\n", (long)addr, length);
	
	struct stub_stack *stub_stack = proc->arch.host->stub_stack;
	struct stub_frame *frame = current_stub_frame (stub_stack);
	
	frame->eax = __NR_munmap;
	frame->ebx = (long)addr;
	frame->ecx = length;
	return host_syscall_in_child (proc);
}

int 
host_assign (struct proc_struct *proc,
			 uintptr_t addr, uint32_t data) 
{
	if (!user_mem_check(proc->mm, addr, sizeof (uintptr_t), 1)) {
		cprintf ("try assigning invalid address: 0x%x\n", addr);
        return -1;
    }

	/* Use stub call so that the page table will be modified properly */
	struct stub_stack *stub_stack = proc->arch.host->stub_stack;
	struct stub_frame *frame = current_stub_frame (stub_stack);
	frame->eax = -1;
	frame->ebx = addr;
	frame->ecx = data;
	return host_syscall_in_child (proc);
	/* return syscall4 (__NR_ptrace, PTRACE_POKEDATA, proc->arch.host_pid, addr, data); */
}

int
host_getvalue (struct proc_struct *proc,
			   uintptr_t addr, uint32_t* data) 
{
	/* cprintf ("got addr 0x%x in host_getvalue\n", addr); */
	/* cprintf ("current host pid = %d\n", proc->arch.host_pid); */
	if (data == NULL)
		return -1;
	if (!user_mem_check(proc->mm, addr, sizeof (uintptr_t), 0)) {
        return -1;
    }
	//cprintf ("get value at address 0x%x\n", addr);
	
	struct stub_stack *stub_stack = proc->arch.host->stub_stack;
	struct stub_frame *frame = current_stub_frame (stub_stack);
	frame->eax = 0;
	frame->ebx = addr;
	*data = host_syscall_in_child (proc);
	//cprintf ("get value done\n");
	return 0;
	//return syscall4 (__NR_ptrace, PTRACE_PEEKDATA, proc->arch.host_pid, addr, 0);
}
